# -*- coding: utf-8 -*-
"""
@Time : 2025/9/23  14:34
@Author : Shang
@File : main.py
@IDE : PyCharm
FastAPI app bootstrap for Day 1
- 挂载静态文件 ../public
- index.html放置根目录
- 定义骨架 /api/chat, /api/tts, /api/asr
"""
import os
import math
from typing import List, Optional

from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, JSONResponse, Response
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

app = FastAPI()

# 解析公共目录 （../public 相对于此文件）
BASE_DIR = os.path.dirname(__file__)                 # .../cn/study/app
STUDY_DIR = os.path.dirname(BASE_DIR)                # .../cn/study
PUBLIC_DIR = os.path.join(STUDY_DIR, "public")      # .../cn/study/public
INDEX_HTML = os.path.join(PUBLIC_DIR, "index.html")

# 挂载静态目录
if os.path.isdir(PUBLIC_DIR):
    app.mount("/public", StaticFiles(directory=PUBLIC_DIR), name="public")

# index.html放在网址根目录上
@app.get("/")
def serve_index():
    if os.path.isfile(INDEX_HTML):
        return FileResponse(INDEX_HTML, media_type="text/html")
    return JSONResponse({"error": "index.html not found"}, status_code=404)

# 模型
class ChatMessage(BaseModel):
    role: str
    content: str

class ChatRequest(BaseModel):
    roleKey: str
    messages: List[ChatMessage] = []

class ChatResponse(BaseModel):
    reply: str
    prosody: str
    citation: Optional[str] = None

class TTSRequest(BaseModel):
    text: str
    roleKey: str
    prosody: str = "neutral"

class ASRResponse(BaseModel):
    text: str

# 角色
ROLE_NAME_MAP = {
    "harry_potter": "喜羊羊",
    "socrates": "灰太狼",
    "sherlock": "光头强",
}

def make_placeholder_reply(req: ChatRequest) -> ChatResponse:
    role_name = ROLE_NAME_MAP.get(req.roleKey, req.roleKey)
    last_user = next((m.content for m in reversed(req.messages) if m.role == "user"), "")
    base = f"【{role_name}】收到：{last_user or '你好'}"
    return ChatResponse(
        reply=base + "（占位回复，LLM 尚未接入）",
        prosody="neutral",
        citation="占位提示：LLM 未配置，当前为规则驱动的示例回复"
    )

# 最小 WAV 发生器 (mono, 16kHz, 16-bit)
def generate_beep_wav(duration_sec: float = 0.3, freq_hz: float = 440.0, sample_rate: int = 16000) -> bytes:
    num_samples = int(duration_sec * sample_rate)
    amplitude = 0.3  # keep low to avoid clipping
    pcm16 = bytearray()
    for n in range(num_samples):
        t = n / sample_rate
        sample = amplitude * math.sin(2 * math.pi * freq_hz * t)
        # convert to 16-bit signed
        s = int(max(-1.0, min(1.0, sample)) * 32767)
        pcm16 += s.to_bytes(2, byteorder="little", signed=True)
    data_size = len(pcm16)

    # WAV header
    byte_rate = sample_rate * 1 * 2
    block_align = 1 * 2
    header = bytearray()
    header += b"RIFF"
    header += (36 + data_size).to_bytes(4, "little")
    header += b"WAVE"
    header += b"fmt "
    header += (16).to_bytes(4, "little")       # PCM header size
    header += (1).to_bytes(2, "little")         # Audio format (PCM)
    header += (1).to_bytes(2, "little")         # Num channels
    header += (sample_rate).to_bytes(4, "little")
    header += (byte_rate).to_bytes(4, "little")
    header += (block_align).to_bytes(2, "little")
    header += (16).to_bytes(2, "little")        # Bits per sample
    header += b"data"
    header += (data_size).to_bytes(4, "little")

    return bytes(header + pcm16)

# 骨架流程
@app.post("/api/chat")
async def api_chat(payload: ChatRequest) -> ChatResponse:
    # Day 1: 返回占位符以确保端到端路径正常工作
    return make_placeholder_reply(payload)

@app.post("/api/tts")
async def api_tts(payload: TTSRequest):
    # Day 1: 返回占位符 WAV ，以便前端可以播放音频
    wav_bytes = generate_beep_wav(duration_sec=0.25, freq_hz=500.0)
    return Response(content=wav_bytes, media_type="audio/wav")

@app.post("/api/asr")
async def api_asr(request: Request):
    # 接受应用程序/八位字节流或音频/wav 并返回占位符文本
    body = await request.body()
    if not body:
        return JSONResponse({"error": "empty audio"}, status_code=400)
    # Day 1: 返回静态文本以关闭循环
    return JSONResponse(ASRResponse(text="（占位）已收到语音流，示例识别文本：你好！").dict())

#入口
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8001)


